package utils

import (
	"context"
	"errors"
	grpc2 "gitee.com/zxs-micro/zxs-micro-common/grpc"
	"gitee.com/zxs-micro/zxs-micro-common/grpc/clients"
	"gitee.com/zxs-micro/zxs-micro-common/grpc/protos/common"
	"google.golang.org/grpc"
	"google.golang.org/grpc/connectivity"
	grpchealth "google.golang.org/grpc/health/grpc_health_v1"
	"math/rand"
	"sync"
	"time"
)

type CommonMicroClient struct {
	gc grpchealth.HealthClient
	lc common.ServerLoadClient
	clients.CommonClients
}

var cs = make(map[string]*CommonMicroClient)
var clock sync.Mutex

func GenerateMicroClients(addr string) (*CommonMicroClient, error) {
	var err error
	clock.Lock()
	cli, ok := cs[addr]
	clock.Unlock()
	if !ok {
		cli = new(CommonMicroClient)
		cli.ClientConn, err = grpc.Dial(addr, grpc2.GetClientDialOpts()...)
		if err != nil {
			return nil, err
		}
		cli.gc = grpchealth.NewHealthClient(cli.ClientConn)
		cli.lc = common.NewServerLoadClient(cli.ClientConn)
		cli.RemoteAddr = addr
		clock.Lock()
		cs[addr] = cli
		clock.Unlock()
	}
	return cli, nil
}

func (m *CommonMicroClient) HealthCheck() error {
	err := m.CheckConn()
	if err != nil {
		return err
	}
	//健康检查
	resp, err := m.gc.Check(context.Background(), &grpchealth.HealthCheckRequest{
		Service: "healths",
	})
	if err != nil {
		return err
	}
	if resp.Status != grpchealth.HealthCheckResponse_SERVING {
		return errors.New("服务健康状态不正常！当前服务健康状态为" + grpchealth.HealthCheckResponse_ServingStatus_name[int32(resp.Status)])
	}
	return nil
}

func (c *CommonMicroClient) CheckConn() (err error) {
	c.ClientLock.Lock()
	if c.ClientConn.GetState() == connectivity.Shutdown {
		c.ClientConn, err = grpc.Dial(c.RemoteAddr, grpc2.GetClientDialOpts()...)
		c.gc = grpchealth.NewHealthClient(c.ClientConn)
		c.lc = common.NewServerLoadClient(c.ClientConn)
	}
	c.ClientLock.Unlock()
	return
}

func (c *CommonMicroClient) ShutdownClient() {
	c.ClientLock.Lock()
	c.ClientConn.Close()
	c.ClientLock.Unlock()
	clock.Lock()
	delete(cs, c.RemoteAddr)
	clock.Unlock()
}

func (m *CommonMicroClient) GetServiceLoad() ([]byte, error) {
	err := m.CheckConn()
	if err != nil {
		return nil, err
	}
	//获取各个服务器的负载状态，来进行负载均衡操作
	resp, err := m.lc.Load(context.Background(), &common.ServerLoadReq{
		Current: true,
	})
	if err != nil {
		return nil, err
	}
	return resp.Loads, nil
}

func init() {
	rand.Seed(time.Now().Unix())
}

func GetRandomInt(limit int) int {
	return rand.Intn(limit)
}
